package org.openfdtd.core3d;

import com.sun.j3d.utils.picking.PickCanvas;
import com.sun.j3d.utils.picking.PickResult;
import com.sun.j3d.utils.picking.PickTool;
import java.awt.event.MouseEvent;
import java.util.Enumeration;
import javax.media.j3d.Appearance;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.Canvas3D;
import javax.media.j3d.Geometry;
import javax.media.j3d.GeometryArray;
import javax.media.j3d.Group;
import javax.media.j3d.LineArray;
import javax.media.j3d.LineStripArray;
import javax.media.j3d.Node;
import javax.media.j3d.Shape3D;
import javax.media.j3d.TriangleArray;
import javax.vecmath.Point3d;

/**
 *
 * @author Dórian C. Langbeck
 */
final class Util {
    private Util() {}

    public static Appearance enableWrite(Appearance app) {
        app.setCapability(Appearance.ALLOW_TEXGEN_WRITE);
        app.setCapability(Appearance.ALLOW_TEXTURE_WRITE);
        app.setCapability(Appearance.ALLOW_MATERIAL_WRITE);
        app.setCapability(Appearance.ALLOW_LINE_ATTRIBUTES_WRITE);
        app.setCapability(Appearance.ALLOW_POINT_ATTRIBUTES_WRITE);
        app.setCapability(Appearance.ALLOW_POLYGON_ATTRIBUTES_WRITE);
        app.setCapability(Appearance.ALLOW_TEXTURE_ATTRIBUTES_WRITE);
        app.setCapability(Appearance.ALLOW_TEXTURE_UNIT_STATE_WRITE);
        app.setCapability(Appearance.ALLOW_COLORING_ATTRIBUTES_WRITE);
        app.setCapability(Appearance.ALLOW_RENDERING_ATTRIBUTES_WRITE);
        app.setCapability(Appearance.ALLOW_TRANSPARENCY_ATTRIBUTES_WRITE);

        return app;
    }

    public static Shape3D enableWrite(Shape3D shape) {
        shape.setCapability(Shape3D.ALLOW_GEOMETRY_WRITE);
        shape.setCapability(Shape3D.ALLOW_APPEARANCE_WRITE);
        shape.setCapability(Shape3D.ALLOW_COLLISION_BOUNDS_WRITE);
        shape.setCapability(Shape3D.ALLOW_APPEARANCE_OVERRIDE_WRITE);

        return shape;
    }

    public static Group enableWrite(Group group) {
        group.setCapability(Group.ALLOW_CHILDREN_WRITE);
        group.setCapability(Group.ALLOW_COLLISION_BOUNDS_WRITE);

        return group;
    }

    public static GeometryArray enableWrite(GeometryArray geom) {
        geom.setCapability(GeometryArray.ALLOW_COLOR_WRITE);
        geom.setCapability(GeometryArray.ALLOW_COUNT_WRITE);
        geom.setCapability(GeometryArray.ALLOW_NORMAL_WRITE);
        geom.setCapability(GeometryArray.ALLOW_REF_DATA_WRITE);
        geom.setCapability(GeometryArray.ALLOW_TEXCOORD_WRITE);
        geom.setCapability(GeometryArray.ALLOW_COORDINATE_WRITE);
        geom.setCapability(GeometryArray.ALLOW_VERTEX_ATTR_WRITE);

        return geom;
    }

    public static void enablePicking(Node node) {
        if (node instanceof SceneComponent)
            if (!((SceneComponent) node).isPickable())
                return;
        
        node.setPickable(true);
        node.setCapability(Node.ENABLE_PICK_REPORTING);
        if (node instanceof Group) {
            Group group = (Group) node;
            Enumeration<Node> nodes = group.getAllChildren();
            while (nodes.hasMoreElements())
                enablePicking(nodes.nextElement());

        } else if (node instanceof Shape3D) {
            Shape3D shape = (Shape3D) node;
            PickTool.setCapabilities(node, PickTool.INTERSECT_FULL);
            Enumeration<Geometry> geom = shape.getAllGeometries();
            while (geom.hasMoreElements())
                ((Geometry) geom.nextElement()).setCapability(Geometry.ALLOW_INTERSECT);
        }
    }

    public static void changeAppearance(Group group, Appearance app) {
        if (group instanceof SceneComponent)
            return;
        
        Enumeration<Node> childs = group.getAllChildren();
        while (childs.hasMoreElements()) {
            Node child = childs.nextElement();
            if (child instanceof Shape3D)
                ((Shape3D) child).setAppearance(app);

            else if (child instanceof Group)
                changeAppearance((Group) child, app);
        }
    }

    public static void changeSceneComponentAppearance(SceneComponent sc, Appearance app) {
        Enumeration<Node> childs = sc.getAllChildren();
        while (childs.hasMoreElements()) {
            Node child = childs.nextElement();
            if (child instanceof Shape3D)
                ((Shape3D) child).setAppearance(app);

            else if (child instanceof Group)
                changeAppearance((Group) child, app);
        }
    }

    public static LineArray buildEdgeGeometry(Edge[] edges) {
        LineArray la = (LineArray) enableWrite(new LineArray(edges.length * 2, GeometryArray.COORDINATES));
        for (int i = 0; i < edges.length; i++)
            la.setCoordinates(i * 2, edges[i].getVertexes());
        return la;
    }

    public static void updateEdgeGeometry(LineArray la, Edge[] edges) {
        for (int i = 0; i < edges.length; i++)
            la.setCoordinates(i * 2, edges[i].getVertexes());
    }
    
    public static TriangleArray buildSurfaceGeometry(Vertex[] v) {
        TriangleArray ta = new TriangleArray(9, GeometryArray.COORDINATES);
        ta.setCoordinates(0, v, 0, 3);
        ta.setCoordinates(3, v, 2, 2);
        ta.setCoordinates(5, v, 0, 1);
        
        return ta;
    }

    public static void updateSurfaceGeometry(TriangleArray ta, Vertex[] v) {
        ta.setCoordinates(0, v, 0, 3);
        ta.setCoordinates(3, v, 2, 2);
        ta.setCoordinates(5, v, 0, 1);
    }

    public static BranchGroup pickBrachGroup(PickCanvas pCanvas, MouseEvent e) {
        pCanvas.setShapeLocation(e);

        //FIX: Pegar todos em ordem para exluir objetos nao "pickables"
        PickResult[] results = pCanvas.pickAllSorted();
        if (results == null)
            return null;

        for (PickResult result : results)
            if (result != null) {
                BranchGroup group = (BranchGroup) result.getNode(PickResult.BRANCH_GROUP);
                if (group != null)
                    return group;
            }
        
        return null;
    }

    public static EditableObject3D pickObject3D(PickCanvas pCanvas, MouseEvent e) {
        BranchGroup group = pickBrachGroup(pCanvas, e);
        if (group == null)
            return null;

        if (group instanceof EditableObject3D)
            return (EditableObject3D) group;

        Node lastNode = group.getParent();
        while (true) {
            if (lastNode == null)
                return null;

            if (lastNode instanceof EditableObject3D)
                return (EditableObject3D) lastNode;
            lastNode = lastNode.getParent();
        }
    }

    public static Surface pickSurface(PickCanvas pCanvas, MouseEvent e) {
        BranchGroup group = pickBrachGroup(pCanvas, e);
        if (group == null)
            return null;

        if (group instanceof Surface)
            return (Surface) group;

        Node lastNode = group.getParent();
        while (true) {
            if (lastNode == null)
                return null;

            if (lastNode instanceof Surface)
                return (Surface) lastNode;
            lastNode = lastNode.getParent();
        }
    }

    public static EditableObject3D getObject3D(Surface face) {
        if (face == null)
            return null;
        
        Node lastNode = face.getParent();
        while (true) {
            if (lastNode == null)
                return null;

            if (lastNode instanceof EditableObject3D)
                return (EditableObject3D) lastNode;
            lastNode = lastNode.getParent();
        }
    }

    public static Point3d getPoint3D(Canvas3D canvas, MouseEvent me) {
        return getPoint3D(canvas, me.getX(), me.getY());
    }

    public static Point3d getPoint3D(Canvas3D canvas, int x, int y) {
        Point3d p = new Point3d();
        canvas.getPixelLocationInImagePlate(x, y, p);
        return p;
    }
}
